library(tidyverse) # for data cleaning and plotting
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.5 ✓ purrr 0.3.4
## ✓ tibble 3.1.6 ✓ dplyr 1.0.7
## ✓ tidyr 1.1.4 ✓ stringr 1.4.0
## ✓ readr 2.1.1 ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(gardenR) # for Lisa's garden data
library(lubridate) # for date manipulation
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
library(openintro) # for the abbr2state() function
## Loading required package: airports
## Loading required package: cherryblossom
## Loading required package: usdata
library(palmerpenguins)# for Palmer penguin data
library(maps) # for map data
##
## Attaching package: 'maps'
## The following object is masked from 'package:purrr':
##
## map
library(ggmap) # for mapping points on maps
## Google's Terms of Service: https://cloud.google.com/maps-platform/terms/.
## Please cite ggmap if you use it! See citation("ggmap") for details.
library(gplots) # for col2hex() function
##
## Attaching package: 'gplots'
## The following object is masked from 'package:stats':
##
## lowess
library(RColorBrewer) # for color palettes
library(sf) # for working with spatial data
## Linking to GEOS 3.9.1, GDAL 3.4.0, PROJ 8.1.1; sf_use_s2() is TRUE
library(leaflet) # for highly customizable mapping
library(ggthemes) # for more themes (including theme_map())
library(plotly) # for the ggplotly() - basic interactivity
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggmap':
##
## wind
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
library(gganimate) # for adding animation layers to ggplots
library(transformr) # for "tweening" (gganimate)
##
## Attaching package: 'transformr'
## The following object is masked from 'package:sf':
##
## st_normalize
library(gifski) # need the library for creating gifs but don't need to load each time
library(shiny) # for creating interactive apps
library(flair)
library(ggthemes) # for more themes (including theme_map())
library(babynames)
##
## Attaching package: 'babynames'
## The following object is masked from 'package:openintro':
##
## births
theme_set(theme_minimal())
# SNCF Train data
small_trains <- read_csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2019/2019-02-26/small_trains.csv")
## Rows: 32772 Columns: 13
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (4): service, departure_station, arrival_station, delay_cause
## dbl (9): year, month, journey_time_avg, total_num_trips, avg_delay_all_depar...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Lisa's garden data
data("garden_harvest")
# Lisa's Mallorca cycling data
mallorca_bike_day7 <- read_csv("https://www.dropbox.com/s/zc6jan4ltmjtvy0/mallorca_bike_day7.csv?dl=1") %>%
select(1:4, speed)
## Rows: 3210 Columns: 11
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (8): lon, lat, ele, extensions, ele.num, time_hr, dist_km, speed
## dttm (2): time, hrminsec
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Heather Lendway's Ironman 70.3 Pan Am championships Panama data
panama_swim <- read_csv("https://raw.githubusercontent.com/llendway/gps-data/master/data/panama_swim_20160131.csv")
## Rows: 36 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): event
## dbl (3): lon, lat, extensions
## lgl (1): ele
## dttm (2): time, hrminsec
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
panama_bike <- read_csv("https://raw.githubusercontent.com/llendway/gps-data/master/data/panama_bike_20160131.csv")
## Rows: 7924 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): event
## dbl (4): lon, lat, ele, extensions
## dttm (2): time, hrminsec
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
panama_run <- read_csv("https://raw.githubusercontent.com/llendway/gps-data/master/data/panama_run_20160131.csv")
## Rows: 1111 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): event
## dbl (4): lon, lat, ele, extensions
## dttm (2): time, hrminsec
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#COVID-19 data from the New York Times
covid19 <- read_csv("https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-states.csv")
## Rows: 41894 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (2): state, fips
## dbl (2): cases, deaths
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Read in the data for the week
breed_traits <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-02-01/breed_traits.csv')
## Rows: 195 Columns: 17
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Breed, Coat Type, Coat Length
## dbl (14): Affectionate With Family, Good With Young Children, Good With Othe...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
trait_description <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-02-01/trait_description.csv')
## Rows: 16 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (4): Trait, Trait_1, Trait_5, Description
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
breed_rank_all <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-02-01/breed_rank.csv')
## Rows: 195 Columns: 11
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Breed, links, Image
## dbl (8): 2013 Rank, 2014 Rank, 2015 Rank, 2016 Rank, 2017 Rank, 2018 Rank, 2...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
data("babynames")
Put your homework on GitHub!
Go here or to previous homework to remind yourself how to get set up.
Once your repository is created, you should always open your project rather than just opening an .Rmd file. You can do that by either clicking on the .Rproj file in your repository folder on your computer. Or, by going to the upper right hand corner in R Studio and clicking the arrow next to where it says Project: (None). You should see your project come up in that list if you’ve used it recently. You could also go to File –> Open Project and navigate to your .Rproj file.
Instructions
Put your name at the top of the document.
For ALL graphs, you should include appropriate labels and alt text.
Feel free to change the default theme, which I currently have set to theme_minimal().
Use good coding practice. Read the short sections on good code with pipes and ggplot2. This is part of your grade!
NEW!! With animated graphs, add eval=FALSE to the code chunk that creates the animation and saves it using anim_save(). Add another code chunk to reread the gif back into the file. See the tutorial for help.
When you are finished with ALL the exercises, uncomment the options at the top so your document looks nicer. Don’t do it before then, or else you might miss some important warnings and messages.
Warm-up exercises from tutorial
- Choose 2 graphs you have created for ANY assignment in this class and add interactivity using the
ggplotly() function.
top_10 <- breed_rank_all %>%
slice(1:10) %>%
select(Breed, `2016 Rank`:`2020 Rank`)
new_top_10 <- top_10 %>%
pivot_longer(ends_with("Rank"), #using pivot_longer to make dataset longer. Reduces columns (turns years into individual row values). Years as variables/columns turns into values of year variable.
names_to = "year",
values_to = "rank") %>%
mutate(year = str_remove(year, " Rank")) %>% #Str function removes Rank from year values.
mutate(year = as.numeric(year)) %>% #turns string for year into numeric data.
group_by(Breed)
top10 <- new_top_10 %>%
ggplot(aes(x = year, y = rank, group = Breed)) +
geom_line(aes(color = fct_reorder2(Breed, year, rank), alpha = 1), size = 2) +
geom_point(aes(color = fct_reorder2(Breed, year, rank), alpha = 1), size = 4) +
#scale_y_continuous(breaks = 1:nrow(new_top_10)) +
scale_y_reverse(breaks = 1:nrow(new_top_10)) + #above reverses order on graph, but still in wrong order on legend...
geom_label(data = new_top_10 %>% filter(year == "2020"), aes(label = Breed, x = 2019.5), size = 2, hjust = 0.5, fontface = "bold") +
theme(legend.position = "none") +
labs(title = "Current Top 10 Dog Breeds Since 2016",
x = "Year",
y = "Ranking",
color = "Breeds")
ggplotly(top10)
## Warning in geom2trace.default(dots[[1L]][[1L]], dots[[2L]][[1L]], dots[[3L]][[1L]]): geom_GeomLabel() has yet to be implemented in plotly.
## If you'd like to see this geom implemented,
## Please open an issue with your example code at
## https://github.com/ropensci/plotly/issues
#COVID-19 data from the New York Times
covid19 <- read_csv("https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-states.csv")
## Rows: 41894 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (2): state, fips
## dbl (2): cases, deaths
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
covid <- covid19 %>%
filter(state %in% c("Minnesota", "Wisconsin", "Iowa", "North Dakota", "South Dakota")) %>%
ggplot(aes(x=date, y=cases, color=state)) +
geom_line() +
labs(title = "COVID-19 Case Count for Midwestern States",
x = "Date",
y = "Cumulative Cases") +
theme(plot.background = element_rect(fill = "snow1"),
text = element_text(family = "Times")) +
scale_y_continuous(labels = scales :: comma)
ggplotly(covid)
- Use animation to tell an interesting story with the
small_trains dataset that contains data from the SNCF (National Society of French Railways). These are Tidy Tuesday data! Read more about it here.

Garden data
- In this exercise, you will create a stacked area plot that reveals itself over time (see the
geom_area() examples here). You will look at cumulative harvest of tomato varieties over time. You should do the following:
- From the
garden_harvest data, filter the data to the tomatoes and find the daily harvest in pounds for each variety.
- Then, for each variety, find the cumulative harvest in pounds.
- Use the data you just made to create a static cumulative harvest area plot, with the areas filled with different colors for each variety and arranged (HINT:
fct_reorder()) from most to least harvested weights (most on the bottom).
- Add animation to reveal the plot over date.
I have started the code for you below. The complete() function creates a row for all unique date/variety combinations. If a variety is not harvested on one of the harvest dates in the dataset, it is filled with a value of 0.
## Maps, animation, and movement!
- Map Lisa’s
mallorca_bike_day7 bike ride using animation! Requirements:
- Plot on a map using
ggmap.
- Show “current” location with a red point.
- Show path up until the current point.
- Color the path according to elevation.
- Show the time in the subtitle.
- CHALLENGE: use the
ggimage package and geom_image to add a bike image instead of a red point. You can use this image. See here for an example.
- Add something of your own! And comment on if you prefer this to the static map and why or why not.
# Lisa's Mallorca cycling data
mallorca_bike_day7 <- read_csv("https://www.dropbox.com/s/zc6jan4ltmjtvy0/mallorca_bike_day7.csv?dl=1") %>%
select(1:4, speed)
## Rows: 3210 Columns: 11
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (8): lon, lat, ele, extensions, ele.num, time_hr, dist_km, speed
## dttm (2): time, hrminsec
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
5. In this exercise, you get to meet Lisa’s sister, Heather! She is a proud Mac grad, currently works as a Data Scientist where she uses R everyday, and for a few years (while still holding a full-time job) she was a pro triathlete. You are going to map one of her races. The data from each discipline of the Ironman 70.3 Pan Am championships, Panama is in a separate file - panama_swim, panama_bike, and panama_run. Create a similar map to the one you created with my cycling data. You will need to make some small changes: 1. combine the files putting them in swim, bike, run order (HINT: bind_rows()), 2. make the leading dot a different color depending on the event (for an extra challenge, make it a different image using `geom_image()!), 3. CHALLENGE (optional): color by speed, which you will need to compute on your own from the data. You can read Heather’s race report here. She is also in the Macalester Athletics Hall of Fame and still has records at the pool.

COVID-19 data
- In this exercise you will animate a map of the US, showing how cumulative COVID-19 cases per 10,000 residents has changed over time. This is similar to exercises 11 & 12 from the previous exercises, with the added animation! So, in the end, you should have something like the static map you made there, but animated over all the days. The code below gives the population estimates for each state and loads the
states_map data. Here is a list of details you should include in the plot:
- Put date in the subtitle.
- Because there are so many dates, you are going to only do the animation for the the 15th of each month. So, filter only to those dates - there are some lubridate functions that can help you do this.
- Use the
animate() function to make the animation 200 frames instead of the default 100 and to pause for 10 frames on the end frame.
- Use
group = date in aes().
- Comment on what you see.

LS0tCnRpdGxlOiAnV2Vla2x5IEV4ZXJjaXNlcyAjNScKYXV0aG9yOiAiQW5uYSBMZWlkbmVyIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICBrZWVwX21kOiBUUlVFCiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQoja25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBlcnJvcj1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFKQpgYGAKCgpgYGB7ciBsaWJyYXJpZXN9CmxpYnJhcnkodGlkeXZlcnNlKSAgICAgIyBmb3IgZGF0YSBjbGVhbmluZyBhbmQgcGxvdHRpbmcKbGlicmFyeShnYXJkZW5SKSAgICAgICAjIGZvciBMaXNhJ3MgZ2FyZGVuIGRhdGEKbGlicmFyeShsdWJyaWRhdGUpICAgICAjIGZvciBkYXRlIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KG9wZW5pbnRybykgICAgICMgZm9yIHRoZSBhYmJyMnN0YXRlKCkgZnVuY3Rpb24KbGlicmFyeShwYWxtZXJwZW5ndWlucykjIGZvciBQYWxtZXIgcGVuZ3VpbiBkYXRhCmxpYnJhcnkobWFwcykgICAgICAgICAgIyBmb3IgbWFwIGRhdGEKbGlicmFyeShnZ21hcCkgICAgICAgICAjIGZvciBtYXBwaW5nIHBvaW50cyBvbiBtYXBzCmxpYnJhcnkoZ3Bsb3RzKSAgICAgICAgIyBmb3IgY29sMmhleCgpIGZ1bmN0aW9uCmxpYnJhcnkoUkNvbG9yQnJld2VyKSAgIyBmb3IgY29sb3IgcGFsZXR0ZXMKbGlicmFyeShzZikgICAgICAgICAgICAjIGZvciB3b3JraW5nIHdpdGggc3BhdGlhbCBkYXRhCmxpYnJhcnkobGVhZmxldCkgICAgICAgIyBmb3IgaGlnaGx5IGN1c3RvbWl6YWJsZSBtYXBwaW5nCmxpYnJhcnkoZ2d0aGVtZXMpICAgICAgIyBmb3IgbW9yZSB0aGVtZXMgKGluY2x1ZGluZyB0aGVtZV9tYXAoKSkKbGlicmFyeShwbG90bHkpICAgICAgICAjIGZvciB0aGUgZ2dwbG90bHkoKSAtIGJhc2ljIGludGVyYWN0aXZpdHkKbGlicmFyeShnZ2FuaW1hdGUpICAgICAjIGZvciBhZGRpbmcgYW5pbWF0aW9uIGxheWVycyB0byBnZ3Bsb3RzCmxpYnJhcnkodHJhbnNmb3JtcikgICAgIyBmb3IgInR3ZWVuaW5nIiAoZ2dhbmltYXRlKQpsaWJyYXJ5KGdpZnNraSkgICAgICAgICMgbmVlZCB0aGUgbGlicmFyeSBmb3IgY3JlYXRpbmcgZ2lmcyBidXQgZG9uJ3QgbmVlZCB0byBsb2FkIGVhY2ggdGltZQpsaWJyYXJ5KHNoaW55KSAgICAgICAgICMgZm9yIGNyZWF0aW5nIGludGVyYWN0aXZlIGFwcHMKbGlicmFyeShmbGFpcikKbGlicmFyeShnZ3RoZW1lcykgICAgICAjIGZvciBtb3JlIHRoZW1lcyAoaW5jbHVkaW5nIHRoZW1lX21hcCgpKQpsaWJyYXJ5KGJhYnluYW1lcykKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkKYGBgCgoKYGBge3IgZGF0YX0KIyBTTkNGIFRyYWluIGRhdGEKc21hbGxfdHJhaW5zIDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMTkvMjAxOS0wMi0yNi9zbWFsbF90cmFpbnMuY3N2IikgCgojIExpc2EncyBnYXJkZW4gZGF0YQpkYXRhKCJnYXJkZW5faGFydmVzdCIpCgojIExpc2EncyBNYWxsb3JjYSBjeWNsaW5nIGRhdGEKbWFsbG9yY2FfYmlrZV9kYXk3IDwtIHJlYWRfY3N2KCJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zL3pjNmphbjRsdG1qdHZ5MC9tYWxsb3JjYV9iaWtlX2RheTcuY3N2P2RsPTEiKSAlPiUgCiAgc2VsZWN0KDE6NCwgc3BlZWQpCgojIEhlYXRoZXIgTGVuZHdheSdzIElyb25tYW4gNzAuMyBQYW4gQW0gY2hhbXBpb25zaGlwcyBQYW5hbWEgZGF0YQpwYW5hbWFfc3dpbSA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2xsZW5kd2F5L2dwcy1kYXRhL21hc3Rlci9kYXRhL3BhbmFtYV9zd2ltXzIwMTYwMTMxLmNzdiIpCgpwYW5hbWFfYmlrZSA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2xsZW5kd2F5L2dwcy1kYXRhL21hc3Rlci9kYXRhL3BhbmFtYV9iaWtlXzIwMTYwMTMxLmNzdiIpCgpwYW5hbWFfcnVuIDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbGxlbmR3YXkvZ3BzLWRhdGEvbWFzdGVyL2RhdGEvcGFuYW1hX3J1bl8yMDE2MDEzMS5jc3YiKQoKI0NPVklELTE5IGRhdGEgZnJvbSB0aGUgTmV3IFlvcmsgVGltZXMKY292aWQxOSA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL255dGltZXMvY292aWQtMTktZGF0YS9tYXN0ZXIvdXMtc3RhdGVzLmNzdiIpCgojIFJlYWQgaW4gdGhlIGRhdGEgZm9yIHRoZSB3ZWVrCmJyZWVkX3RyYWl0cyA8LSByZWFkcjo6cmVhZF9jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkvbWFzdGVyL2RhdGEvMjAyMi8yMDIyLTAyLTAxL2JyZWVkX3RyYWl0cy5jc3YnKQp0cmFpdF9kZXNjcmlwdGlvbiA8LSByZWFkcjo6cmVhZF9jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkvbWFzdGVyL2RhdGEvMjAyMi8yMDIyLTAyLTAxL3RyYWl0X2Rlc2NyaXB0aW9uLmNzdicpCmJyZWVkX3JhbmtfYWxsIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIyLzIwMjItMDItMDEvYnJlZWRfcmFuay5jc3YnKQoKZGF0YSgiYmFieW5hbWVzIikKCmBgYAoKCiMjIFB1dCB5b3VyIGhvbWV3b3JrIG9uIEdpdEh1YiEKCkdvIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vbGxlbmR3YXkvZ2l0aHViX2Zvcl9jb2xsYWJvcmF0aW9uL2Jsb2IvbWFzdGVyL2dpdGh1Yl9mb3JfY29sbGFib3JhdGlvbi5tZCkgb3IgdG8gcHJldmlvdXMgaG9tZXdvcmsgdG8gcmVtaW5kIHlvdXJzZWxmIGhvdyB0byBnZXQgc2V0IHVwLiAKCk9uY2UgeW91ciByZXBvc2l0b3J5IGlzIGNyZWF0ZWQsIHlvdSBzaG91bGQgYWx3YXlzIG9wZW4geW91ciAqKnByb2plY3QqKiByYXRoZXIgdGhhbiBqdXN0IG9wZW5pbmcgYW4gLlJtZCBmaWxlLiBZb3UgY2FuIGRvIHRoYXQgYnkgZWl0aGVyIGNsaWNraW5nIG9uIHRoZSAuUnByb2ogZmlsZSBpbiB5b3VyIHJlcG9zaXRvcnkgZm9sZGVyIG9uIHlvdXIgY29tcHV0ZXIuIE9yLCBieSBnb2luZyB0byB0aGUgdXBwZXIgcmlnaHQgaGFuZCBjb3JuZXIgaW4gUiBTdHVkaW8gYW5kIGNsaWNraW5nIHRoZSBhcnJvdyBuZXh0IHRvIHdoZXJlIGl0IHNheXMgUHJvamVjdDogKE5vbmUpLiBZb3Ugc2hvdWxkIHNlZSB5b3VyIHByb2plY3QgY29tZSB1cCBpbiB0aGF0IGxpc3QgaWYgeW91J3ZlIHVzZWQgaXQgcmVjZW50bHkuIFlvdSBjb3VsZCBhbHNvIGdvIHRvIEZpbGUgLS0+IE9wZW4gUHJvamVjdCBhbmQgbmF2aWdhdGUgdG8geW91ciAuUnByb2ogZmlsZS4gCgojIyBJbnN0cnVjdGlvbnMKCiogUHV0IHlvdXIgbmFtZSBhdCB0aGUgdG9wIG9mIHRoZSBkb2N1bWVudC4gCgoqICoqRm9yIEFMTCBncmFwaHMsIHlvdSBzaG91bGQgaW5jbHVkZSBhcHByb3ByaWF0ZSBsYWJlbHMgYW5kIGFsdCB0ZXh0LioqIAoKKiBGZWVsIGZyZWUgdG8gY2hhbmdlIHRoZSBkZWZhdWx0IHRoZW1lLCB3aGljaCBJIGN1cnJlbnRseSBoYXZlIHNldCB0byBgdGhlbWVfbWluaW1hbCgpYC4gCgoqIFVzZSBnb29kIGNvZGluZyBwcmFjdGljZS4gUmVhZCB0aGUgc2hvcnQgc2VjdGlvbnMgb24gZ29vZCBjb2RlIHdpdGggW3BpcGVzXShodHRwczovL3N0eWxlLnRpZHl2ZXJzZS5vcmcvcGlwZXMuaHRtbCkgYW5kIFtnZ3Bsb3QyXShodHRwczovL3N0eWxlLnRpZHl2ZXJzZS5vcmcvZ2dwbG90Mi5odG1sKS4gKipUaGlzIGlzIHBhcnQgb2YgeW91ciBncmFkZSEqKgoKKiAqKk5FVyEhKiogV2l0aCBhbmltYXRlZCBncmFwaHMsIGFkZCBgZXZhbD1GQUxTRWAgdG8gdGhlIGNvZGUgY2h1bmsgdGhhdCBjcmVhdGVzIHRoZSBhbmltYXRpb24gYW5kIHNhdmVzIGl0IHVzaW5nIGBhbmltX3NhdmUoKWAuIEFkZCBhbm90aGVyIGNvZGUgY2h1bmsgdG8gcmVyZWFkIHRoZSBnaWYgYmFjayBpbnRvIHRoZSBmaWxlLiBTZWUgdGhlIFt0dXRvcmlhbF0oaHR0cHM6Ly9hbmltYXRpb24tYW5kLWludGVyYWN0aXZpdHktaW4tci5uZXRsaWZ5LmFwcC8pIGZvciBoZWxwLiAKCiogV2hlbiB5b3UgYXJlIGZpbmlzaGVkIHdpdGggQUxMIHRoZSBleGVyY2lzZXMsIHVuY29tbWVudCB0aGUgb3B0aW9ucyBhdCB0aGUgdG9wIHNvIHlvdXIgZG9jdW1lbnQgbG9va3MgbmljZXIuIERvbid0IGRvIGl0IGJlZm9yZSB0aGVuLCBvciBlbHNlIHlvdSBtaWdodCBtaXNzIHNvbWUgaW1wb3J0YW50IHdhcm5pbmdzIGFuZCBtZXNzYWdlcy4KCiMjIFdhcm0tdXAgZXhlcmNpc2VzIGZyb20gdHV0b3JpYWwKCiAgMS4gQ2hvb3NlIDIgZ3JhcGhzIHlvdSBoYXZlIGNyZWF0ZWQgZm9yIEFOWSBhc3NpZ25tZW50IGluIHRoaXMgY2xhc3MgYW5kIGFkZCBpbnRlcmFjdGl2aXR5IHVzaW5nIHRoZSBgZ2dwbG90bHkoKWAgZnVuY3Rpb24uCiAgCmBgYHtyfQp0b3BfMTAgPC0gYnJlZWRfcmFua19hbGwgJT4lIAogIHNsaWNlKDE6MTApICU+JQogIHNlbGVjdChCcmVlZCwgYDIwMTYgUmFua2A6YDIwMjAgUmFua2ApIAogIApuZXdfdG9wXzEwIDwtIHRvcF8xMCAlPiUgCiAgcGl2b3RfbG9uZ2VyKGVuZHNfd2l0aCgiUmFuayIpLCAjdXNpbmcgcGl2b3RfbG9uZ2VyIHRvIG1ha2UgZGF0YXNldCBsb25nZXIuIFJlZHVjZXMgY29sdW1ucyAodHVybnMgeWVhcnMgaW50byBpbmRpdmlkdWFsIHJvdyB2YWx1ZXMpLiBZZWFycyBhcyB2YXJpYWJsZXMvY29sdW1ucyB0dXJucyBpbnRvIHZhbHVlcyBvZiB5ZWFyIHZhcmlhYmxlLiAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAieWVhciIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAicmFuayIpICU+JSAKICBtdXRhdGUoeWVhciA9IHN0cl9yZW1vdmUoeWVhciwgIiBSYW5rIikpICU+JSAjU3RyIGZ1bmN0aW9uIHJlbW92ZXMgUmFuayBmcm9tIHllYXIgdmFsdWVzLiAKICBtdXRhdGUoeWVhciA9IGFzLm51bWVyaWMoeWVhcikpICU+JSAjdHVybnMgc3RyaW5nIGZvciB5ZWFyIGludG8gbnVtZXJpYyBkYXRhLiAKICBncm91cF9ieShCcmVlZCkgCgp0b3AxMCA8LSBuZXdfdG9wXzEwICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSByYW5rLCBncm91cCA9IEJyZWVkKSkgKwogIGdlb21fbGluZShhZXMoY29sb3IgPSBmY3RfcmVvcmRlcjIoQnJlZWQsIHllYXIsIHJhbmspLCBhbHBoYSA9IDEpLCBzaXplID0gMikgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZmN0X3Jlb3JkZXIyKEJyZWVkLCB5ZWFyLCByYW5rKSwgYWxwaGEgPSAxKSwgc2l6ZSA9IDQpICsKICAjc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IDE6bnJvdyhuZXdfdG9wXzEwKSkgKwogIHNjYWxlX3lfcmV2ZXJzZShicmVha3MgPSAxOm5yb3cobmV3X3RvcF8xMCkpICsgI2Fib3ZlIHJldmVyc2VzIG9yZGVyIG9uIGdyYXBoLCBidXQgc3RpbGwgaW4gd3Jvbmcgb3JkZXIgb24gbGVnZW5kLi4uCiAgZ2VvbV9sYWJlbChkYXRhID0gbmV3X3RvcF8xMCAlPiUgZmlsdGVyKHllYXIgPT0gIjIwMjAiKSwgYWVzKGxhYmVsID0gQnJlZWQsIHggPSAyMDE5LjUpLCBzaXplID0gMiwgaGp1c3QgPSAwLjUsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgbGFicyh0aXRsZSA9ICJDdXJyZW50IFRvcCAxMCBEb2cgQnJlZWRzIFNpbmNlIDIwMTYiLCAKICAgICAgIHggPSAiWWVhciIsIAogICAgICAgeSA9ICJSYW5raW5nIiwgCiAgICAgICBjb2xvciA9ICJCcmVlZHMiKQoKZ2dwbG90bHkodG9wMTApCmBgYAoKYGBge3J9CiNDT1ZJRC0xOSBkYXRhIGZyb20gdGhlIE5ldyBZb3JrIFRpbWVzCmNvdmlkMTkgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ueXRpbWVzL2NvdmlkLTE5LWRhdGEvbWFzdGVyL3VzLXN0YXRlcy5jc3YiKQpgYGAKCmBgYHtyfQpjb3ZpZCA8LSBjb3ZpZDE5ICU+JSAKICBmaWx0ZXIoc3RhdGUgJWluJSBjKCJNaW5uZXNvdGEiLCAiV2lzY29uc2luIiwgIklvd2EiLCAiTm9ydGggRGFrb3RhIiwgIlNvdXRoIERha290YSIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1jYXNlcywgY29sb3I9c3RhdGUpKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSAiQ09WSUQtMTkgQ2FzZSBDb3VudCBmb3IgTWlkd2VzdGVybiBTdGF0ZXMiLCAKICAgICAgIHggPSAiRGF0ZSIsIAogICAgICAgeSA9ICJDdW11bGF0aXZlIENhc2VzIikgKwogICAgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAic25vdzEiKSwKICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJUaW1lcyIpKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzIDo6IGNvbW1hKQoKZ2dwbG90bHkoY292aWQpCmBgYAoKIDIuIFVzZSBhbmltYXRpb24gdG8gdGVsbCBhbiBpbnRlcmVzdGluZyBzdG9yeSB3aXRoIHRoZSBgc21hbGxfdHJhaW5zYCBkYXRhc2V0IHRoYXQgY29udGFpbnMgZGF0YSBmcm9tIHRoZSBTTkNGIChOYXRpb25hbCBTb2NpZXR5IG9mIEZyZW5jaCBSYWlsd2F5cykuIFRoZXNlIGFyZSBUaWR5IFR1ZXNkYXkgZGF0YSEgUmVhZCBtb3JlIGFib3V0IGl0IFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L3RyZWUvbWFzdGVyL2RhdGEvMjAxOS8yMDE5LTAyLTI2KS4KCmBgYHtyIHRyYWluMSwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0Kc21hbGxfdHJhaW5zICU+JQogIGZpbHRlcih5ZWFyID09ICIyMDE3IikgJT4lCiAgZmlsdGVyKGRlcGFydHVyZV9zdGF0aW9uID09ICJTQUlOVCBFVElFTk5FIENIQVRFQVVDUkVVWCIpICU+JQogICNmaWx0ZXIoYXJyaXZhbF9zdGF0aW9uKQogIAogICNncm91cF9ieShkZXBhcnR1cmVfc3RhdGlvbikgJT4lCiAgbXV0YXRlKGxhdGVfYXJyaXZhbF9wZXIgPSAobnVtX2Fycml2aW5nX2xhdGUvdG90YWxfbnVtX3RyaXBzKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdG90YWxfbnVtX3RyaXBzLCAKICAgICAgICAgICAgIHkgPSBsYXRlX2Fycml2YWxfcGVyKSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJUb3RhbCB0cmlwcyBmcm9tIFNhaW50IEV0aWVubmUgU3RhdGlvbiBpbiAyMDE3IiwKICAgICAgIHN1YnRpdGxlID0gIk1vbnRoOiB7Y2xvc2VzdF9zdGF0ZX0iLAogICAgICAgeSA9ICJMYXRlIEFycml2YWwgUGVyY2VudGFnZSIsCiAgICAgICB4ID0gIlRvdGFsIFRyaXBzIikgKwogIHRyYW5zaXRpb25fc3RhdGVzKG1vbnRoKQpgYGAKCgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0KYW5pbV9zYXZlKCJ0cmFpbjEuZ2lmIikKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoInRyYWluMS5naWYiKQpgYGAKCiMjIEdhcmRlbiBkYXRhCgogIDMuIEluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIGNyZWF0ZSBhIHN0YWNrZWQgYXJlYSBwbG90IHRoYXQgcmV2ZWFscyBpdHNlbGYgb3ZlciB0aW1lIChzZWUgdGhlIGBnZW9tX2FyZWEoKWAgZXhhbXBsZXMgW2hlcmVdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9wb3NpdGlvbl9zdGFjay5odG1sKSkuIFlvdSB3aWxsIGxvb2sgYXQgY3VtdWxhdGl2ZSBoYXJ2ZXN0IG9mIHRvbWF0byB2YXJpZXRpZXMgb3ZlciB0aW1lLiBZb3Ugc2hvdWxkIGRvIHRoZSBmb2xsb3dpbmc6CiAgKiBGcm9tIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGRhdGEsIGZpbHRlciB0aGUgZGF0YSB0byB0aGUgdG9tYXRvZXMgYW5kIGZpbmQgdGhlICpkYWlseSogaGFydmVzdCBpbiBwb3VuZHMgZm9yIGVhY2ggdmFyaWV0eS4gIAogICogVGhlbiwgZm9yIGVhY2ggdmFyaWV0eSwgZmluZCB0aGUgY3VtdWxhdGl2ZSBoYXJ2ZXN0IGluIHBvdW5kcy4gIAogICogVXNlIHRoZSBkYXRhIHlvdSBqdXN0IG1hZGUgdG8gY3JlYXRlIGEgc3RhdGljIGN1bXVsYXRpdmUgaGFydmVzdCBhcmVhIHBsb3QsIHdpdGggdGhlIGFyZWFzIGZpbGxlZCB3aXRoIGRpZmZlcmVudCBjb2xvcnMgZm9yIGVhY2ggdmFyaWV0eSBhbmQgYXJyYW5nZWQgKEhJTlQ6IGBmY3RfcmVvcmRlcigpYCkgZnJvbSBtb3N0IHRvIGxlYXN0IGhhcnZlc3RlZCB3ZWlnaHRzIChtb3N0IG9uIHRoZSBib3R0b20pLiAgCiAgKiBBZGQgYW5pbWF0aW9uIHRvIHJldmVhbCB0aGUgcGxvdCBvdmVyIGRhdGUuIAoKSSBoYXZlIHN0YXJ0ZWQgdGhlIGNvZGUgZm9yIHlvdSBiZWxvdy4gVGhlIGBjb21wbGV0ZSgpYCBmdW5jdGlvbiBjcmVhdGVzIGEgcm93IGZvciBhbGwgdW5pcXVlIGBkYXRlYC9gdmFyaWV0eWAgY29tYmluYXRpb25zLiBJZiBhIHZhcmlldHkgaXMgbm90IGhhcnZlc3RlZCBvbiBvbmUgb2YgdGhlIGhhcnZlc3QgZGF0ZXMgaW4gdGhlIGRhdGFzZXQsIGl0IGlzIGZpbGxlZCB3aXRoIGEgdmFsdWUgb2YgMC4KCmBgYHtyIHZlZzEsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9CmdhcmRlbl9oYXJ2ZXN0ICU+JSAKICBmaWx0ZXIodmVnZXRhYmxlID09ICJ0b21hdG9lcyIpICU+JSAKICBncm91cF9ieShkYXRlLCB2YXJpZXR5KSAlPiUgCiAgc3VtbWFyaXplKGRhaWx5X2hhcnZlc3RfbGIgPSBzdW0od2VpZ2h0KSowLjAwMjIwNDYyKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBjb21wbGV0ZSh2YXJpZXR5LCAKICAgICAgICAgICBkYXRlLCAKICAgICAgICAgICBmaWxsID0gbGlzdChkYWlseV9oYXJ2ZXN0X2xiID0gMCkpICU+JQogIGdyb3VwX2J5KHZhcmlldHkpICU+JQogIG11dGF0ZShjdW1faGFydmVzdF9sYiA9IGN1bXN1bShkYWlseV9oYXJ2ZXN0X2xiKSkgJT4lCiAgZ2dwbG90KGFlcyh5ID0gY3VtX2hhcnZlc3RfbGIsIAogICAgICAgICAgICAgeCA9IGRhdGUsIAogICAgICAgICAgICAgZmlsbCA9IHZhcmlldHkpKSArCiAgZ2VvbV9hcmVhKCkgKwogIGxhYnModGl0bGUgPSAiQ3VtdWxhdGl2ZSBUb21hdG8gSGFydmVzdCBieSBWYXJpZXR5IiwgCiAgICAgICB4ID0gIkRhdGUiLCAKICAgICAgIHkgPSAiQ3VtdWxhdGl2ZSBIYXJ2ZXN0IChsYnMpIikgKwogIHRyYW5zaXRpb25fcmV2ZWFsKGRhdGUpCiAgCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQphbmltX3NhdmUoInZlZzEuZ2lmIikKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoInZlZzEuZ2lmIikKYGBgCiMjIE1hcHMsIGFuaW1hdGlvbiwgYW5kIG1vdmVtZW50IQoKICA0LiBNYXAgTGlzYSdzIGBtYWxsb3JjYV9iaWtlX2RheTdgIGJpa2UgcmlkZSB1c2luZyBhbmltYXRpb24hIAogIFJlcXVpcmVtZW50czoKICAqIFBsb3Qgb24gYSBtYXAgdXNpbmcgYGdnbWFwYC4gIAogICogU2hvdyAiY3VycmVudCIgbG9jYXRpb24gd2l0aCBhIHJlZCBwb2ludC4gCiAgKiBTaG93IHBhdGggdXAgdW50aWwgdGhlIGN1cnJlbnQgcG9pbnQuICAKICAqIENvbG9yIHRoZSBwYXRoIGFjY29yZGluZyB0byBlbGV2YXRpb24uICAKICAqIFNob3cgdGhlIHRpbWUgaW4gdGhlIHN1YnRpdGxlLiAgCiAgKiBDSEFMTEVOR0U6IHVzZSB0aGUgYGdnaW1hZ2VgIHBhY2thZ2UgYW5kIGBnZW9tX2ltYWdlYCB0byBhZGQgYSBiaWtlIGltYWdlIGluc3RlYWQgb2YgYSByZWQgcG9pbnQuIFlvdSBjYW4gdXNlIFt0aGlzXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbGxlbmR3YXkvYW5pbWF0aW9uX2FuZF9pbnRlcmFjdGl2aXR5L21hc3Rlci9iaWtlLnBuZykgaW1hZ2UuIFNlZSBbaGVyZV0oaHR0cHM6Ly9nb29kZWthdC5naXRodWIuaW8vcHJlc2VudGF0aW9ucy8yMDE5LWlzdWdnLWdnYW5pbWF0ZS1zcG9va3kvc2xpZGVzLmh0bWwjMzUpIGZvciBhbiBleGFtcGxlLiAKICAqIEFkZCBzb21ldGhpbmcgb2YgeW91ciBvd24hIEFuZCBjb21tZW50IG9uIGlmIHlvdSBwcmVmZXIgdGhpcyB0byB0aGUgc3RhdGljIG1hcCBhbmQgd2h5IG9yIHdoeSBub3QuCgpgYGB7cn0KIyBMaXNhJ3MgTWFsbG9yY2EgY3ljbGluZyBkYXRhCm1hbGxvcmNhX2Jpa2VfZGF5NyA8LSByZWFkX2NzdigiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy96YzZqYW40bHRtanR2eTAvbWFsbG9yY2FfYmlrZV9kYXk3LmNzdj9kbD0xIikgJT4lIAogIHNlbGVjdCgxOjQsIHNwZWVkKQpgYGAKCmBgYHtyIG1hbGxvcmNhLCBldmFsID0gRkFMU0UsIGVjaG8gPSBGQUxTRX0KbWFsbG9yY2FfbWFwIDwtIGdldF9zdGFtZW5tYXAoCiAgICBiYm94ID0gYyhsZWZ0ID0gMi4yOTAyLCBib3R0b20gPSAzOS40NDk4LCByaWdodCA9IDIuODU5NSwgdG9wID0gMzkuNzgxNCksIAogICAgbWFwdHlwZSA9ICJ0ZXJyYWluIiwKICAgIHpvb20gPSAxMQopCmdnbWFwKG1hbGxvcmNhX21hcCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IG1hbGxvcmNhX2Jpa2VfZGF5NywgCiAgICAgICAgICAgICBhZXMoeCA9IGxvbiwgCiAgICAgICAgICAgICAgICAgeSA9IGxhdCksIAogICAgICAgICAgICAgc2l6ZSA9IDUsCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSBtYWxsb3JjYV9iaWtlX2RheTcsIAogICAgICAgICAgICAgYWVzKHggPSBsb24sCiAgICAgICAgICAgICAgICAgeSA9IGxhdCwgCiAgICAgICAgICAgICAgICAgY29sb3IgPSBlbGUpLAogICAgICAgICAgICAgc2l6ZSA9IC41KSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJtYWdtYSIpICsKICBsYWJzKHRpdGxlID0gIk1hbGxvcmNhIEJpa2UgUmlkZSIsIAogICAgICAgc3VidGl0bGUgPSAiVGltZToge2ZyYW1lX2Fsb25nfSIsIAogICAgICAgeCA9ICIiLCAKICAgICAgIHkgPSAiIikgKwogIHRyYW5zaXRpb25fcmV2ZWFsKHRpbWUpICsKICB0aGVtZV9tYXAoKSArCiAgdGhlbWUobGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQphbmltX3NhdmUoIm1hbGxvcmNhLmdpZiIpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJtYWxsb3JjYS5naWYiKQpgYGAKIDUuIEluIHRoaXMgZXhlcmNpc2UsIHlvdSBnZXQgdG8gbWVldCBMaXNhJ3Mgc2lzdGVyLCBIZWF0aGVyISBTaGUgaXMgYSBwcm91ZCBNYWMgZ3JhZCwgY3VycmVudGx5IHdvcmtzIGFzIGEgRGF0YSBTY2llbnRpc3Qgd2hlcmUgc2hlIHVzZXMgUiBldmVyeWRheSwgYW5kIGZvciBhIGZldyB5ZWFycyAod2hpbGUgc3RpbGwgaG9sZGluZyBhIGZ1bGwtdGltZSBqb2IpIHNoZSB3YXMgYSBwcm8gdHJpYXRobGV0ZS4gWW91IGFyZSBnb2luZyB0byBtYXAgb25lIG9mIGhlciByYWNlcy4gVGhlIGRhdGEgZnJvbSBlYWNoIGRpc2NpcGxpbmUgb2YgdGhlIElyb25tYW4gNzAuMyBQYW4gQW0gY2hhbXBpb25zaGlwcywgUGFuYW1hIGlzIGluIGEgc2VwYXJhdGUgZmlsZSAtIGBwYW5hbWFfc3dpbWAsIGBwYW5hbWFfYmlrZWAsIGFuZCBgcGFuYW1hX3J1bmAuIENyZWF0ZSBhIHNpbWlsYXIgbWFwIHRvIHRoZSBvbmUgeW91IGNyZWF0ZWQgd2l0aCBteSBjeWNsaW5nIGRhdGEuIFlvdSB3aWxsIG5lZWQgdG8gbWFrZSBzb21lIHNtYWxsIGNoYW5nZXM6IDEuIGNvbWJpbmUgdGhlIGZpbGVzIHB1dHRpbmcgdGhlbSBpbiBzd2ltLCBiaWtlLCBydW4gb3JkZXIgKEhJTlQ6IGBiaW5kX3Jvd3MoKWApLCAyLiBtYWtlIHRoZSBsZWFkaW5nIGRvdCBhIGRpZmZlcmVudCBjb2xvciBkZXBlbmRpbmcgb24gdGhlIGV2ZW50IChmb3IgYW4gZXh0cmEgY2hhbGxlbmdlLCBtYWtlIGl0IGEgZGlmZmVyZW50IGltYWdlIHVzaW5nIGBnZW9tX2ltYWdlKCkhKSwgMy4gQ0hBTExFTkdFIChvcHRpb25hbCk6IGNvbG9yIGJ5IHNwZWVkLCB3aGljaCB5b3Ugd2lsbCBuZWVkIHRvIGNvbXB1dGUgb24geW91ciBvd24gZnJvbSB0aGUgZGF0YS4gWW91IGNhbiByZWFkIEhlYXRoZXIncyByYWNlIHJlcG9ydCBbaGVyZV0oaHR0cHM6Ly9oZWF0aGVybGVuZHdheS5jb20vMjAxNi8wMi8xMC9pcm9ubWFuLTcwLTMtcGFuLWFtZXJpY2FuLWNoYW1waW9uc2hpcHMtcGFuYW1hLXJhY2UtcmVwb3J0LykuIFNoZSBpcyBhbHNvIGluIHRoZSBNYWNhbGVzdGVyIEF0aGxldGljcyBbSGFsbCBvZiBGYW1lXShodHRwczovL2F0aGxldGljcy5tYWNhbGVzdGVyLmVkdS9ob25vcnMvaGFsbC1vZi1mYW1lL2hlYXRoZXItbGVuZHdheS8xODQpIGFuZCBzdGlsbCBoYXMgcmVjb3JkcyBhdCB0aGUgcG9vbC4gCiAgCmBgYHtyIHRyaSwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0KdHJpZGF0YSA8LSBiaW5kX3Jvd3MocGFuYW1hX3N3aW0sIHBhbmFtYV9iaWtlLCBwYW5hbWFfcnVuKQoKcGFuYW1hX21hcCA8LSBnZXRfc3RhbWVubWFwKAogICAgYmJveCA9IGMobGVmdCA9IC03OS41ODY0LCBib3R0b20gPSA4Ljg1MDAsIHJpZ2h0ID0gLTc5LjQzNDAsIHRvcCA9IDkuMDYyNiksIAogICAgbWFwdHlwZSA9ICJ0ZXJyYWluIiwKICAgIHpvb20gPSAxMgopCgpnZ21hcChwYW5hbWFfbWFwKSArCiAgZ2VvbV9wb2ludChkYXRhID0gdHJpZGF0YSwgCiAgICAgICAgICAgICBhZXMoeCA9IGxvbiwgCiAgICAgICAgICAgICAgICAgeSA9IGxhdCwgCiAgICAgICAgICAgICAgICAgY29sb3IgPSBldmVudCksIAogICAgICAgICAgICAgICAgIHNpemUgPSA1KSArCiAgZ2VvbV9wYXRoKGRhdGEgPSB0cmlkYXRhLCAKICAgICAgICAgICAgIGFlcyh4ID0gbG9uLAogICAgICAgICAgICAgICAgIHkgPSBsYXQpLAogICAgICAgICAgICAgc2l6ZSA9IC41KSArCiAgI3NjYWxlX2NvbG9yX3ZpcmlkaXNfYyhvcHRpb24gPSAibWFnbWEiKSArCiAgbGFicyh0aXRsZSA9ICJJcm9ubWFuIDcwLjMgUGFuIEFtIENoYW1waW9uc2hpcHMiLCAKICAgICAgIHN1YnRpdGxlID0gIlRpbWU6IHtmcmFtZV9hbG9uZ30iLCAKICAgICAgIHggPSAiIiwgCiAgICAgICB5ID0gIiIpICsKICB0cmFuc2l0aW9uX3JldmVhbCh0aW1lKSArCiAgdGhlbWVfbWFwKCkgKwogIHRoZW1lKGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQoKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0KYW5pbV9zYXZlKCJ0cmkuZ2lmIikKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoInRyaS5naWYiKQpgYGAKCiMjIENPVklELTE5IGRhdGEKCiAgNi4gSW4gdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCBhbmltYXRlIGEgbWFwIG9mIHRoZSBVUywgc2hvd2luZyBob3cgY3VtdWxhdGl2ZSBDT1ZJRC0xOSBjYXNlcyBwZXIgMTAsMDAwIHJlc2lkZW50cyBoYXMgY2hhbmdlZCBvdmVyIHRpbWUuIFRoaXMgaXMgc2ltaWxhciB0byBleGVyY2lzZXMgMTEgJiAxMiBmcm9tIHRoZSBwcmV2aW91cyBleGVyY2lzZXMsIHdpdGggdGhlIGFkZGVkIGFuaW1hdGlvbiEgU28sIGluIHRoZSBlbmQsIHlvdSBzaG91bGQgaGF2ZSBzb21ldGhpbmcgbGlrZSB0aGUgc3RhdGljIG1hcCB5b3UgbWFkZSB0aGVyZSwgYnV0IGFuaW1hdGVkIG92ZXIgYWxsIHRoZSBkYXlzLiBUaGUgY29kZSBiZWxvdyBnaXZlcyB0aGUgcG9wdWxhdGlvbiBlc3RpbWF0ZXMgZm9yIGVhY2ggc3RhdGUgYW5kIGxvYWRzIHRoZSBgc3RhdGVzX21hcGAgZGF0YS4gSGVyZSBpcyBhIGxpc3Qgb2YgZGV0YWlscyB5b3Ugc2hvdWxkIGluY2x1ZGUgaW4gdGhlIHBsb3Q6CiAgCiAgKiBQdXQgZGF0ZSBpbiB0aGUgc3VidGl0bGUuICAgCiAgKiBCZWNhdXNlIHRoZXJlIGFyZSBzbyBtYW55IGRhdGVzLCB5b3UgYXJlIGdvaW5nIHRvIG9ubHkgZG8gdGhlIGFuaW1hdGlvbiBmb3IgdGhlIHRoZSAxNXRoIG9mIGVhY2ggbW9udGguIFNvLCBmaWx0ZXIgb25seSB0byB0aG9zZSBkYXRlcyAtIHRoZXJlIGFyZSBzb21lIGx1YnJpZGF0ZSBmdW5jdGlvbnMgdGhhdCBjYW4gaGVscCB5b3UgZG8gdGhpcy4gICAKICAqIFVzZSB0aGUgYGFuaW1hdGUoKWAgZnVuY3Rpb24gdG8gbWFrZSB0aGUgYW5pbWF0aW9uIDIwMCBmcmFtZXMgaW5zdGVhZCBvZiB0aGUgZGVmYXVsdCAxMDAgYW5kIHRvIHBhdXNlIGZvciAxMCBmcmFtZXMgb24gdGhlIGVuZCBmcmFtZS4gICAKICAqIFVzZSBgZ3JvdXAgPSBkYXRlYCBpbiBgYWVzKClgLiAgIAogICogQ29tbWVudCBvbiB3aGF0IHlvdSBzZWUuICAKCmBgYHtyIGNvdmlkMTksIGV2YWwgPSBGQUxTRSwgZWNobyA9IEZBTFNFfQpjZW5zdXNfcG9wX2VzdF8yMDE4IDwtIHJlYWRfY3N2KCJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zLzZ0eHd2M2I0bmc3cGVwZS91c19jZW5zdXNfMjAxOF9zdGF0ZV9wb3BfZXN0LmNzdj9kbD0xIikgJT4lIAogIHNlcGFyYXRlKHN0YXRlLCBpbnRvID0gYygiZG90Iiwic3RhdGUiKSwgZXh0cmEgPSAibWVyZ2UiKSAlPiUgCiAgc2VsZWN0KC1kb3QpICU+JSAKICBtdXRhdGUoc3RhdGUgPSBzdHJfdG9fbG93ZXIoc3RhdGUpKQoKc3RhdGVzX21hcCA8LSBtYXBfZGF0YSgic3RhdGUiKQoKCmNvdmlkMTlfc3RhdGUgPC0gY292aWQxOSAlPiUKICBncm91cF9ieShzdGF0ZSkgJT4lCiAgCiAgYXJyYW5nZShkZXNjKGRhdGUpKSAlPiUKICAjbXV0YXRlKGNhc2VzID0gYXMuY2hhcmFjdGVyKGNhc2VzKSkgJT4lCiAgbXV0YXRlKGNhc2VzID0gYXMuaW50ZWdlcihjYXNlcykpICU+JQogIG11dGF0ZShzdGF0ZSA9IHRvbG93ZXIoc3RhdGUpKSAKICAjc2xpY2UoMSkgCgpjb3ZpZDE5XzEwMDAwIDwtICNjcmVhdGVzIG5ldyBkYXRhc2V0IAogIGNvdmlkMTlfc3RhdGUgJT4lIAogIGxlZnRfam9pbihjZW5zdXNfcG9wX2VzdF8yMDE4LAogICAgICAgICAgICBieSA9IGMoInN0YXRlIikpICAgI2pvaW5zIHN0YXJidWNrc191c19ieV9zdGF0ZSBkYXRhc2V0IHdpdGggdGhlIGNlbnN1c19wb3AgZGF0YXNldC4gc2luY2UgdGhlIHN0YXJidWNrc191cyBkYXRhc2V0IGNhbGxzIHRoZSBzdGF0ZSB2YXJpYWJsZSAic3RhdGVfbmFtZSIgYW5kIHRoZSBjZW5zdXMgZGF0YXNldCBjYWxscyB0aGUgc3RhdGUgdmFyaWFibGUgInN0YXRlIiwgdGhlIGJ5IGxpbmUgbWF0Y2hlcyB0aG9zZSB0d28gdmFyaWFibGVzIHRvIHRoZSBzYW1lIG9uZS4gCiAgI211dGF0ZShzdGFyYnVja3NfcGVyXzEwMDAwID0gKG4vZXN0X3BvcF8yMDE4KSoxMDAwMCkgI2NyZWF0ZXMgdmFyaWFibGUgdG8gc2hvdyB0aGUgbnVtYmVyIG9mIFN0YXJidWNrcyBwZXIgMTAsMDAwIHBlb3BsZSBwZXIgc3RhdGUKCgojZmlsdGVyX3RpbWUoY292aWQxOV8xMDAwMCwgJ2VuZCcgfiAnMTUnKQogIApjb3ZpZDE5XzEwMDAwICU+JSAKICBtdXRhdGUoY2FzZXNfMTAwMDAgPSBjYXNlcy9lc3RfcG9wXzIwMTgqMTAwMDApICU+JQogIGdyb3VwX2J5KHN0YXRlKSAlPiUKICBtdXRhdGUoZGF5ID0gZGF5KGRhdGUpKSAlPiUKICBmaWx0ZXIoZGF5ID09IDE1KSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9tYXAobWFwID0gc3RhdGVzX21hcCwKICAgICAgICAgICBhZXMobWFwX2lkID0gc3RhdGUsCiAgICAgICAgICAgICAgIGZpbGwgPSBjYXNlc18xMDAwMCkpICsKICBleHBhbmRfbGltaXRzKHggPSBzdGF0ZXNfbWFwJGxvbmcsIHkgPSBzdGF0ZXNfbWFwJGxhdCkgKyAKICAjc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gInBsYXNtYSIpICsKICB0aGVtZV9tYXAoKSArCiAgbGFicyh0aXRsZSA9ICJDdW11bGF0aXZlIENPVklEIENhc2VzIHBlciAxMCwwMDAgcGVvcGxlIiwKICAgICAgIHN1YnRpdGxlID0gIlRpbWU6IHtmcmFtZV9hbG9uZ30iLAogICAgICAgZmlsbCA9ICJDYXNlcyBwZXIgMTAsMDAwIFBlb3BsZSIsCiAgICAgICBjYXB0aW9uID0gIlBsb3QgY3JlYXRlZCBieSBBbm5hIExlaWRuZXIiKSArCiAgdHJhbnNpdGlvbl9yZXZlYWwoZGF0ZSkKCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQphbmltX3NhdmUoImNvdmlkMTkuZ2lmIikKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImNvdmlkMTkuZ2lmIikKYGBg